package cn.lisens.demo.service;


import java.time.Duration;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;

import cn.lisens.demo.model.Customer;
import cn.lisens.demo.model.DictType;
import cn.lisens.demo.model.TestWebRequest;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

@Service
public class TestWebClientService {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private final WebClient webClient;
	private static String JSESSIONID = null;

	@Autowired
	public TestWebClientService(WebClient.Builder webClientBuilder) throws SSLException {
		final AuthorizationClient authorizationClient = new AuthorizationClient("https://authorization-url", "authorization-resource", "user", "password");
        final ExchangeFilterFunction authorizationFilter = new AuthorizationFilterFunction(authorizationClient);
        ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication("foo", "bar");
		this.webClient = webClientBuilder
				.filter(auth)
				.filter(tokenFilter())
				.filter(renewTokenFilter())
				.filter(logRequest())
				.build();
	}
	
	public String login(TestWebRequest createRequest) {
        return webClient.post()
                .uri("/login")
//                .body(Mono.just(createRequest), TestWebRequest.class)
                .body(BodyInserters.fromFormData("username", "admin").with("password", "lyxx"))
                .exchangeToMono(response -> {
        			if (response.statusCode().equals(HttpStatus.FOUND)) {
        				if(response.cookies().get("JSESSIONID").get(0) != null) {
        					JSESSIONID = response.cookies().get("JSESSIONID").get(0).getValue();
        				}
        				System.err.println(JSESSIONID);
        				return response.bodyToMono(String.class);
        			}
        			else {
        				return response.createException().flatMap(Mono::error);
//        				return Mono.empty();
        			}
					
        		})
//                .retrieve()
//                .bodyToMono(String.class)
                .block();
    }
	
	public List<Customer> listData(TestWebRequest createRequest) {
        Mono<List<Customer>> customersMono = webClient.get()
                .uri("/customer/listData")
                .accept(MediaType.APPLICATION_JSON)
                .exchangeToMono(response -> {
                    if (response.statusCode().equals(HttpStatus.OK)) {
                        return response.bodyToFlux(Customer.class).collectList();
                    } else {
                    	System.err.println("返回异常！");
                        return response.createException().flatMap(Mono::error);
                    }
                });
//                .bodyToFlux(Customer.class)
//                .collectList();
        Mono<List<DictType>> dictTypesMono = webClient.post()
                .uri("/dictType/listData")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToFlux(DictType.class)
                .collectList();
        
//        customersMono.subscribe(customers->{
//        	for (Customer customer : customers) {
//				System.out.println(customer);
//			}
//        });
        System.out.println("你好，上面的就是返回内容");
//        Map<String, Object> data = Mono.zip(customersMono, dictTypesMono, (person, hobbies) -> {
//		            Map<String, Object> map = new LinkedHashMap<>();
//		            map.put("customers", person);
//		            map.put("dictTypes", hobbies);
//		            return map;
//		        })
//		        .block();
//        System.err.println(data);
//        return (List<Customer>)data.get("customers");
        return customersMono.block();
    }
	
	public String doLogin(TestWebRequest createRequest) {
        return webClient.post()
                .uri("/login")
//                .body(Mono.just(createRequest), TestWebRequest.class)
                .body(BodyInserters.fromFormData("username", "admin").with("password", "lyxx"))
                .exchangeToMono(response -> {
        			return response.bodyToMono(String.class);
        		}).block();
//                .retrieve()
//                .bodyToMono(String.class)
//                .block();
    }
	private ExchangeFilterFunction logRequest() {
        return (clientRequest, next) -> {
        	System.err.println("------------日志开始输出------------");
            logger.info("请求: {} {}", clientRequest.method(), clientRequest.url());
            clientRequest.headers()
                    .forEach((name, values) -> values.forEach(value -> logger.info("header:{}={}", name, value)));
            clientRequest.cookies()
            		.forEach((name, values) -> values.forEach(value -> logger.info("cookies:{}={}", name, value)));
            System.err.println("------------日志输出结束------------");
            return next.exchange(clientRequest);
        };
    }
	
	public Mono<String> newToken(){
		return webClient.post()
                .uri("/login")
                .body(BodyInserters.fromFormData("username", "admin").with("password", "lyxx"))
                .exchangeToMono(response -> {
        			if (response.statusCode().equals(HttpStatus.FOUND)) {
        				JSESSIONID = response.cookies().get("JSESSIONID").get(0).getValue();
        				return Mono.just(JSESSIONID);
        			}
        			return Mono.just("");
        		});
	}
	public ExchangeFilterFunction renewTokenFilter() {
	    return (request, next) -> next.exchange(request).flatMap(response -> {
	    	if (response.statusCode().equals(HttpStatus.FOUND)
	    			&& "/web/login".equals(response.headers().header("Location").get(0))) {
	    		System.err.println("需要登录");
	    		return response.releaseBody()
	    				.then(newToken())
	                    .flatMap(token -> {
	                        ClientRequest newRequest = ClientRequest.from(request)
	                        		.headers(headers -> headers.set("testheader", "test3"))
	                        		.headers(headers -> headers.setBearerAuth("**********"))
	                        		.cookies(cookies -> cookies.set("JSESSIONID", token))
	                        		.build();
	                        return next.exchange(newRequest);
	                    });
	    	}else {
	    		return Mono.just(response);
	    	}
	    });
	}
	public ExchangeFilterFunction tokenFilter() {
		return ExchangeFilterFunction.ofRequestProcessor(req -> {
			return Mono.just(ClientRequest.from(req)
					.header("testheader", "test0")
					.cookie("JSESSIONID", JSESSIONID).build());
		});
	}
	
	public void async() {
		for (int i = 0; i < 1000; i++) {
			System.out.println("第"+i+"次请求");
			Mono<List<Customer>> customersMono = webClient.post()
	                .uri("/customer/listData")
	                .accept(MediaType.APPLICATION_JSON)
	                .retrieve()
	                .bodyToFlux(Customer.class)
	                .collectList();
	        customersMono.subscribe(customers->{
	        	System.out.println("数据已响应，线程Id："+Thread.currentThread().getId()+"   响应内容："+customers.size());
	        });
		}
        System.out.println("等待相应");
    }
	
	public void batchSave() {
		for (int i = 0; i < 500; i++) {
			System.out.println("第"+i+"次请求");
			Customer customer = new Customer();
			customer.setFirstName("姓张"+i);
			customer.setLastName("名三"+i);
			customer.setEmail("test@qq.com");
			Mono<String> customersMono = webClient.post()
	                .uri("/customer/save")
	                .accept(MediaType.TEXT_PLAIN)
	                .body(Mono.just(customer),Customer.class)
	                .retrieve()
	                .onStatus(HttpStatus::is4xxClientError, clientResponse -> handle4xxErrorResponse(clientResponse))
					.onStatus(HttpStatus::is5xxServerError, clientResponse -> handle5xxErrorResponse(clientResponse))
	                .bodyToMono(String.class)
	                .onErrorReturn("请求报错了");
	        customersMono.subscribe(result->{
	        	System.out.println("数据已响应，线程Id："+Thread.currentThread().getId()+"   响应内容："+result);
	        	try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
	        	System.out.println("睡眠结束");
	        });
		}
        System.out.println("等待相应");
    }
	
	public static Retry fixedRetry = Retry.backoff(3,Duration.ofSeconds(2))
			.filter(throwable -> throwable instanceof WebClientResponseException);

//	public static Retry fixedRetry5xx = Retry.anyOf(RuntimeException.class).fixedBackoff(Duration.ofSeconds(2))
//			.retryMax(3).doOnRetry((exception) -> {
//				log.info("Exception is : " + exception);
//			});

	public Mono<RuntimeException> handle4xxErrorResponse(ClientResponse clientResponse) {
		String _function = "handle4xxErrorResponse";
		Mono<String> errorResponse = clientResponse.bodyToMono(String.class);
		return errorResponse.flatMap((message) -> {
			logger.error( _function + "::ErrorResponse Code is: {} and Exception message is : {}",clientResponse.rawStatusCode(),message);
			throw new RuntimeException(message);
		});
	}
	
	public Mono<RuntimeException> handle5xxErrorResponse(ClientResponse clientResponse) {
		String _function = ".handle5xxErrorResponse";
		Mono<String> errorResponse = clientResponse.bodyToMono(String.class);
		return errorResponse.flatMap((message) -> {
			logger.error(_function + "::ErrorResponse Code is: {} and Exception message is : {}",clientResponse.rawStatusCode(),message);
			throw new RuntimeException(message);
		});
	}
	
	// 使用exchange()方法获取完整响应
    public Mono<ResponseEntity<Customer>> getCustomerWithFullResponse(Integer id) {
        return webClient.get()
                .uri("/customer/get?id={id}", id)
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .flatMap(response -> response.toEntity(Customer.class));
    }
}
